﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Json_Test.SerializeClasses;
using ComputerBeacon.Json;
using System.Collections.Generic;

namespace Json_Test {
    [TestClass]
    public class Deserialize {
        static void Verify(Address a, Address b) {
            if (a == null) {
                Assert.IsNull(b);
                return;
            }
            Assert.AreEqual(a.CountryField, b.CountryField);
            Assert.AreEqual(a.StateField, b.StateField);
        }
        static void Verify(User a, User b) {
            Verify(a.AddressProperty, b.AddressProperty);
            Assert.AreEqual(a.IdProperty, b.IdProperty);
            Assert.AreEqual(a.NameProperty, b.NameProperty);
        }

        #region class

        [TestMethod]
        public void FieldsOnly() {
            Address a = new Address() {
                CountryField = "US",
                StateField = "CA"
            };

            Address b = (Address)Serializer.Deserialize(typeof(Address), Serializer.Serialize(a));
            Verify(a, b);
        }

        [TestMethod]
        public void PropertyOnly() {
            var a = new User() { IdProperty = 123, NameProperty = "Hello World" };

            var b = (User)Serializer.Deserialize(typeof(User), Serializer.Serialize(a));
            Verify(a, b);
        }

        [TestMethod]
        public void NestedObject() {
            var a = new User() {
                IdProperty = 123,
                NameProperty = "Hello World",
                AddressProperty = new Address() { CountryField = "US", StateField = "CA" }
            };

            var b = (User)Serializer.Deserialize(typeof(User), Serializer.Serialize(a));
            Verify(a, b);
        }

        [TestMethod]
        public void ArrayProperty() {
            var a = new User();
            a.EmailsField = new string[] { "a@b.c", "d@e.f" };

            var b = (User)Serializer.Deserialize(typeof(User), Serializer.Serialize(a));

            Assert.AreEqual(a.EmailsField.Length, b.EmailsField.Length);
            for (int i = 0; i < a.EmailsField.Length; i++) Assert.AreEqual(a.EmailsField[i], b.EmailsField[i]);
        }

        [TestMethod]
        public void EmptyArrayProperty() {
            var a = new User();
            a.EmailsField = new string[] { };

            var b = (User)Serializer.Deserialize(typeof(User), Serializer.Serialize(a));

            Assert.AreEqual(a.EmailsField.Length, b.EmailsField.Length);
            for (int i = 0; i < a.EmailsField.Length; i++) Assert.AreEqual(a.EmailsField[i], b.EmailsField[i]);
        }
        #endregion

        #region arrays <--> JsonArrays

        [TestMethod]
        public void Array_Integers() {
            int[] array = new int[] { 1, 2, 3, 4, 5, 0 };
            int[] result = (int[])Serializer.Deserialize(typeof(int[]), Serializer.Serialize(array));

            Assert.AreEqual(array.Length, result.Length);
            for (int i = 0; i < array.Length; i++) Assert.AreEqual(array[i], result[i]);
        }

        [TestMethod]
        public void Array_DoubleFromIntegers() {
            var ja = new JsonArray(new object[] { 1, 2, 3, 4, 5 });
            double[] result = Serializer.Deserialize<double[]>(ja.ToString());

            Assert.AreEqual(ja.Count, result.Length);
            for (int i = 0; i < ja.Count; i++) Assert.AreEqual(Convert.ToInt32(ja[i]), Convert.ToInt32(result[i]));
        }

        [TestMethod]
        public void Array_LongFromIntegers() {
            var ja = new JsonArray(new object[] { 1, 2, 3, 4, 5, 2147483648 });
            long[] result = Serializer.Deserialize<long[]>(ja.ToString());

            Assert.AreEqual(ja.Count, result.Length);
            for (int i = 0; i < ja.Count; i++) Assert.AreEqual(Convert.ToInt64(ja[i]), Convert.ToInt64(result[i]));
        }

        [TestMethod]
        public void Array_Doubles() {
            double[] array = new double[] { 1.1, 2.2, 3.3, 4.4, 5.5, 0 };
            double[] result = (double[])Serializer.Deserialize(typeof(double[]), Serializer.Serialize(array));

            Assert.AreEqual(array.Length, result.Length);
            for (int i = 0; i < array.Length; i++) Assert.AreEqual(array[i], result[i]);
        }
        [TestMethod]
        public void Array_Strings() {
            string[] array = new string[] { "Hello World", "Bye World", "~!@#$%^&*()-=_+,./<>?;:'\"" };
            string[] result = (string[])Serializer.Deserialize(typeof(string[]), Serializer.Serialize(array));

            Assert.AreEqual(array.Length, result.Length);
            for (int i = 0; i < array.Length; i++) Assert.AreEqual(array[i], result[i]);
        }
        [TestMethod]
        public void Array_Objects() {
            object[] array = new object[] { "Hello World", true, false, 12345, 3.14159 };
            object[] result = (object[])Serializer.Deserialize(typeof(object[]), Serializer.Serialize(array));

            Assert.AreEqual(array.Length, result.Length);
            for (int i = 0; i < array.Length; i++) Assert.AreEqual(array[i], result[i]);
        }
        [TestMethod]
        public void NestedArrays() {
            int[][] a0 = new int[][] {
                new int[] {0,1,2},
                new int[] {3,4,5},
                new int[]{6},
                new int[]{}
            };

            var result = (int[][])Serializer.Deserialize(typeof(int[][]), Serializer.Serialize(a0));

            Assert.AreEqual(a0.Length, result.Length);
            for (int i = 0; i < a0.Length; i++) {
                Assert.AreEqual(a0[i].Length, result[i].Length);
                for (int j = 0; j < a0[i].Length; j++) Assert.AreEqual(a0[i][j], result[i][j]);
            }
        }

        #endregion

        #region JsonArray <--> List<>
        [TestMethod]
        public void List_Integers() {
            var list = new List<int> { 1, 2, 3, 4, 5 };
            var result = (List<int>)Serializer.Deserialize(typeof(List<int>), Serializer.Serialize(list));

            Assert.AreEqual(list.Count, result.Count);
            for (int i = 0; i < list.Count; i++) Assert.AreEqual(list[i], result[i]);
        }
        [TestMethod]
        public void List_Strings() {
            var list = new List<string> { "Hello World", "早晨", "a@b.c" };
            var result = (List<string>)Serializer.Deserialize(typeof(List<string>), Serializer.Serialize(list));

            Assert.AreEqual(list.Count, result.Count);
            for (int i = 0; i < list.Count; i++) Assert.AreEqual(list[i], result[i]);
        }
        [TestMethod]
        public void List_Classes() {
            var list = new List<User> {
                new User() { IdProperty = 0, NameProperty = "user A" },
                new User() { IdProperty = 1, NameProperty = "user B" },
                new User() { IdProperty = 2, NameProperty = "user C" }
            };
            var result = (List<User>)Serializer.Deserialize(typeof(List<User>), Serializer.Serialize(list));

            Assert.AreEqual(list.Count, result.Count);
            for (int i = 0; i < list.Count; i++) Verify(list[i], result[i]);
        }
        #endregion

        #region CustomType
        [TestMethod]
        public void DateTime() {
            DateTime answer = new DateTime(2013, 1, 1);
            var g = new Game() { StartTime = answer, Score = 123, Position = 3.14159 };
            var serializer = new CustomSerializer();

            var h = (Game)Serializer.Deserialize(typeof(Game), Serializer.Serialize(g, serializer), serializer);
            Assert.AreEqual(g.Score, h.Score);
            Assert.AreEqual(g.StartTime.Ticks, h.StartTime.Ticks);
            Assert.AreEqual(g.Position, h.Position);
        }

        [ExpectedException(typeof(InvalidOperationException))]
        [TestMethod]
        public void Interface() {
            var list = new List<int>(new[] { 1, 2, 3, 4, 5 });
            var list2 = Serializer.Deserialize<IList<int>>(Serializer.Serialize(list));
        }
        #endregion
    }
}
